/******************************************************************
*
*	CyberHTTP for Java
*
*	Copyright (C) Satoshi Konno 2002-2004
*
*	File: HTTPRequest.java
*
*	Revision;
*
*	11/18/02
*		- first revision.
*	05/23/03
*		- Giordano Sassaroli <sassarol@cefriel.it>
*		- Add a relative URL check to setURI().
*	09/02/03
*		- Giordano Sassaroli <sassarol@cefriel.it>
*		- Problem : Devices whose description use absolute urls receive wrong http requests
*		- Error : the presence of a base url is not mandatory, the API code makes the assumption that control and event subscription urls are relative
*		- Description: The method setURI should be changed as follows
*	02/01/04
*		- Added URI parameter methods.
*	03/16/04
*		- Removed setVersion() because the method is added to the super class.
*		- Changed getVersion() to return the version when the first line string has the length.
*	05/19/04
*		- Changed post(HTTPResponse *) to close the socket stream from the server.
*
******************************************************************/

package org.cybergarage.http;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Logger;

import com.cidero.util.UTF8Util;


public class HTTPRequest extends HTTPPacket
{
  private static Logger logger = Logger.getLogger("org.cybergarage.http");

	////////////////////////////////////////////////
	//	Constructor
	////////////////////////////////////////////////
	
	public HTTPRequest()
	{
	}

	public HTTPRequest(InputStream in)
	{
		super(in);
	}

	public HTTPRequest(HTTPSocket httpSock)
	{
		this(httpSock.getInputStream());
		setSocket(httpSock);
	}

	////////////////////////////////////////////////
	//	Method
	////////////////////////////////////////////////

	private String method = null;

	public void setMethod(String value)
	{
		method = value;
	}
		
	public String getMethod()
	{
		if (method != null)
			return method;
		return getFirstLineToken(0);
	}

	public boolean isMethod(String method)
	{
		String headerMethod = getMethod();
		if (headerMethod == null)
			return false;
		return headerMethod.equalsIgnoreCase(method);
	}

	public boolean isGetRequest()
	{
		return isMethod(HTTP.GET);
	}

	public boolean isPostRequest()
	{
		return isMethod(HTTP.POST);
	}

	public boolean isSubscribeRequest()
	{
		return isMethod(HTTP.SUBSCRIBE);
	}

	public boolean isUnsubscribeRequest()
	{
		return isMethod(HTTP.UNSUBSCRIBE);
	}

	public boolean isNotifyRequest()
	{
		return isMethod(HTTP.NOTIFY);
	}
 
	////////////////////////////////////////////////
	//	URI
	////////////////////////////////////////////////

	private String uri = null;

	public void setURI(String value, boolean isCheckRelativeURL)
	{
		uri = value;
		if (isCheckRelativeURL == false)
			return;
		// Thanks for Giordano Sassaroli <sassarol@cefriel.it> (09/02/03)
		uri = HTTP.toRelativeURL(uri);
	}

	public void setURI(String value)
	{
		setURI(value, false);
	}

	public String getURI()
	{
		if (uri != null)
			return uri;
		return getFirstLineToken(1);
	}

	////////////////////////////////////////////////
	//	URI Parameter
	////////////////////////////////////////////////
	
	public ParameterList getParameterList()
	{
		ParameterList paramList = new ParameterList();
		String uri = getURI();
		if (uri == null)
			return paramList;
		int uriLen = uri.length();
		int paramIdx = uri.indexOf('?');
		if (paramIdx < 0)
			return paramList;
		while (0 < paramIdx) {
			int eqIdx = uri.indexOf('=', (paramIdx+1));
			String name = uri.substring(paramIdx+1, eqIdx);
			int nextParamIdx = uri.indexOf('&', (eqIdx+1));
			String value = uri.substring(eqIdx+1, (0 < nextParamIdx) ? nextParamIdx : uri.length());
			Parameter param = new Parameter(name, value);
			paramList.add(param);
			paramIdx = nextParamIdx;
		}
		return paramList;
	}
	
	public String getParameterValue(String name)
	{
		ParameterList paramList = getParameterList();
		return paramList.getValue(name);
	}
	
	////////////////////////////////////////////////
	//	Version
	////////////////////////////////////////////////

	public String getVersion()
	{
		if (hasFirstLine() == true)
			return getFirstLineToken(2);
		return super.getVersion();
	}

	////////////////////////////////////////////////
	//	SOAPAction
	////////////////////////////////////////////////

	public boolean isSOAPAction()
	{
		return hasHeader(HTTP.SOAP_ACTION);
	}

	////////////////////////////////////////////////
	// Host / Port	
	////////////////////////////////////////////////
	
	private String requestHost = "";
	
	public void setRequestHost(String host)
	{
		requestHost = host;
	}

	public String getRequestHost()
	{
		return requestHost;
	}

	private int requestPort = -1;
	
	public void setRequestPort(int host)
	{
		requestPort = host;
	}

	public int getRequestPort()
	{
		return requestPort;
	}
	
	////////////////////////////////////////////////
	//	Socket
	////////////////////////////////////////////////

	private HTTPSocket httpSocket = null;

	private void setSocket(HTTPSocket value)
	{
		httpSocket = value;
	}
		
	public HTTPSocket getSocket()
	{
		return httpSocket;
	}

	/////////////////////////// /////////////////////
	//	local address/port
	////////////////////////////////////////////////

	public String getLocalAddress()
	{
		return getSocket().getLocalAddress();	
	}

	public int getLocalPort()
	{
		return getSocket().getLocalPort();	
	}

	////////////////////////////////////////////////
	//	parseRequest
	////////////////////////////////////////////////

	public boolean parseRequestLine(String lineStr)
	{
		StringTokenizer st = new StringTokenizer(lineStr, HTTP.REQEST_LINE_DELIM);
		if (st.hasMoreTokens() == false)
			return false;
		setMethod(st.nextToken());
		if (st.hasMoreTokens() == false)
			return false;
		setURI(st.nextToken());
		if (st.hasMoreTokens() == false)
			return false;
		setVersion(st.nextToken());
		return true;
     }

	////////////////////////////////////////////////
	//	First Line
	////////////////////////////////////////////////

	public String getFirstLineString()
	{
		return getMethod() + " " + getURI() + " HTTP/" + getVersion() + HTTP.CRLF;
	}

	////////////////////////////////////////////////
	//	getHeader
	////////////////////////////////////////////////
	
	public String getHeader()
	{
		StringBuffer str = new StringBuffer();
		
		str.append(getFirstLineString());
		
		String headerString  = getHeaderString();		
		str.append(headerString);
		
		return str.toString();
	}
	
	////////////////////////////////////////////////
	//	POST
	////////////////////////////////////////////////

	public void post(HTTPResponse httpRes)
	{
		HTTPSocket httpSock = getSocket();
		httpSock.post(httpRes);
		httpSock.close();
	}

	public HTTPResponse post(String host, int port)
	{
		HTTPResponse httpRes = new HTTPResponse();
		
    // Test - Add 'connection: close' to all outgoing HTTP requests
    //setConnection("close");
    
 		try {

      Socket sock;

      //
      // If host is in IP '.' notation, create a InetAddr here and use
      // that to open the socket. This allows the Socket constructor to
      // bypass some name resolution logic than can introduce significant
      // latency under Windows when TCP/IP NETBIOS name resolution is in
      // effect.
      //
      // Split pattern of "\D" means split when not a digit ('.'). Helps
      // screen out non-numeric hosts with '.''s  (e.g. java.sun.com.net) 

      //System.out.println("-!!!---post: Getting new socket addr = " + host );

      String[] hostByteStrings = host.split("\\D");
      if( hostByteStrings.length == 4 )
      {
        byte[] hostBytes = new byte[4];
        for( int n = 0 ; n < 4 ; n++ )
          hostBytes[n] = (byte)Integer.parseInt( hostByteStrings[n] );

        //System.out.println("-!!--post: Creating Inet4Address for " + host );
        InetAddress inetAddr = InetAddress.getByAddress( host, hostBytes );

        //System.out.println("-!!--creating socket " );
        sock = new Socket( inetAddr, port );
      }
      else
      {
        //System.out.println( "hostByteStrings.length == " +  
        //                            hostByteStrings.length );
        sock = new Socket( host, port);
      }

      //System.out.println("-!!!!!!!----Posting request ---------------");

			OutputStream out = sock.getOutputStream();
			PrintStream pout = new PrintStream(out, false, "UTF-8");
			pout.print(getHeader());
			pout.print(HTTP.CRLF);

      String outString = getContentString();
      //System.out.println("-!!!!!----Posting request ---checking UTF-------");
      //System.out.println( "[" + outString + "]\n" );
      //UTF8Util.checkForMultiByteChars( outString );
			pout.print( outString );
			pout.flush();
      //System.out.println("-!!!!!!!----Posted HTTP request ---------------");

			InputStream in = sock.getInputStream();
			httpRes.set(in);			
			in.close();
      //System.out.println("-!!!!!!----Got HTTP response ---------------");

			out.close();
			sock.close();
			
		}
		catch (ConnectException e) {
      logger.warning("Exception posting HTTP request: " + e );
			httpRes.setStatusCode(HTTPStatus.NOT_FOUND);
    }
		catch (Exception e) {
      logger.warning("Exception posting HTTP request: " + e );
			httpRes.setStatusCode(HTTPStatus.INTERNAL_SERVER_ERROR);
		}
		
		return httpRes;
	}

	////////////////////////////////////////////////
	//	set
	////////////////////////////////////////////////

	public void set(HTTPRequest httpReq)
	{
		set((HTTPPacket)httpReq);
		setSocket(httpReq.getSocket());
	}

	////////////////////////////////////////////////
	//	OK/BAD_REQUEST
	////////////////////////////////////////////////

	public void returnResponse(int statusCode)
	{
		HTTPResponse httpRes = new HTTPResponse();
		httpRes.setStatusCode(statusCode);
		httpRes.setContentLength(0);
		post(httpRes);
	}

	public void returnOK()
	{
		returnResponse(HTTPStatus.OK);
	}

	public void returnBadRequest()
	{
		returnResponse(HTTPStatus.BAD_REQUEST);
	}

	////////////////////////////////////////////////
	//	toString
	////////////////////////////////////////////////
	
	public String toString()
	{
		StringBuffer str = new StringBuffer();

		str.append(getHeader());
		str.append(HTTP.CRLF);
		str.append(getContentString());
		
		return str.toString();
	}

	public void print()
	{
		System.out.println(toString());
	}
}
